from typing import Annotated, Optional
from enum import Enum, unique
from datetime import datetime, timezone, timedelta
from sqlmodel import Field, SQLModel, Relationship, Session, select
from sqlalchemy import event, JSON
from sqlalchemy.orm import Mapper

DEFAULT_CARD_RULE = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'


@unique
class AccountIdentity(Enum):
    """账号身份"""
    NORMAL_USER = 0  # 普通用户
    ADMIN_USER = 1  # 普通管理员
    SUPER_USER = 2  # 超级管理员


ALLOW_CREATE_IDENTITY = {AccountIdentity.NORMAL_USER.value, AccountIdentity.ADMIN_USER.value}
ALLOW_UPDATE_IDENTITY = {AccountIdentity.NORMAL_USER.value, AccountIdentity.ADMIN_USER.value,
                         AccountIdentity.SUPER_USER.value}


@unique
class CardStatus(Enum):
    """卡密状态"""
    UNKNOWN = 0  # 未激活
    ACTIVE = 1  # 已激活
    FREEZE = 2  # 已冻结
    EXPIRED = 3  # 已过期


@unique
class DeviceStatus(Enum):
    """设备状态"""
    OFFLINE = 0  # 离线
    ONLINE = 1  # 在线


@unique
class DeviceLogEventType(Enum):
    """设备日志事件类型"""
    LOGIN = 0  # 登录
    HEARTBEAT = 1  # 心跳
    ERROR = 2  # 异常


@unique
class FirewallRuleType(Enum):
    """防火墙规则类型"""
    IP = 0
    DEVICE = 1


def get_now_date(timezone_offset=8):
    """获取当前时间对象"""
    local_timezone = timezone(timedelta(hours=timezone_offset))
    return datetime.now(local_timezone)


class Account(SQLModel, table=True):
    """
    账号模型
    """

    __tablename__ = 'tb_accounts'
    id: Annotated[int, Field(default=None, primary_key=True)]  # 主键 id
    aid: Annotated[str, Field(min_length=12, max_length=12, unique=True)]  # 长度为 12 的字符串
    username: Annotated[str, Field(..., min_length=6, max_length=32, unique=True)]  # 用户名，唯一索引
    avatar: Annotated[str, Field('', max_length=512)]  # 账号头像
    email: Annotated[str, Field(..., max_length=320, unique=True)]  # 绑定邮箱，唯一索引
    phone: Annotated[str, Field('', min_length=8, max_length=24, unique=True)]  # 绑定手机号，唯一索引
    password: Annotated[str, Field(..., min_length=8, max_length=128)]  # 密码（加密后的密码）
    is_banned: Annotated[bool, Field(False)]  # 是否禁用，默认值为 False
    identity: Annotated[int, Field(AccountIdentity.ADMIN_USER.value, ge=0, le=255)]  # 账号身份
    max_apps: Annotated[int, Field(100, ge=0, le=255)]  # 账号可添加应用上限
    login_time: Annotated[datetime, Field(None, nullable=True)]  # 登录时间
    create_time: Annotated[datetime, Field(default_factory=get_now_date)]  # 创建时间
    update_time: Annotated[datetime, Field(default_factory=get_now_date)]  # 更新时间
    remark: Annotated[str, Field('', max_length=512)]  # 账号备注

    apps: list['App'] = Relationship(back_populates='account', cascade_delete=True)
    system_boards: list['SystemBoard'] = Relationship(back_populates='account', cascade_delete=True)

    @property
    def roles(self):
        if self.identity == AccountIdentity.SUPER_USER.value:
            return ['super', 'admin', 'user']
        if self.identity == AccountIdentity.ADMIN_USER.value:
            return ['admin', 'user']
        return ['user']

    @classmethod
    def get_by_aid(cls, session: Session, aid: str):
        return session.exec(
            select(cls).where(cls.aid == aid)
        ).first()

    @property
    def has_admin_permission(self):
        return (self.identity == AccountIdentity.SUPER_USER.value or
                self.identity == AccountIdentity.ADMIN_USER.value)

    @property
    def has_super_permission(self):
        return self.identity == AccountIdentity.SUPER_USER.value


@event.listens_for(Account, 'before_update', propagate=True)
def update_timestamp(_mapper: Mapper, _connection, target: Account):
    """
    在更新记录时，自动将 `update_time` 字段设置为当前时间
    """
    target.update_time = get_now_date()


class App(SQLModel, table=True):
    """
    应用模型
    """
    __tablename__ = "tb_apps"

    id: Annotated[int, Field(default=None, primary_key=True)]  # 主键
    flag: Annotated[str, Field(min_length=20, max_length=20, unique=True)]  # 应用标识
    key: Annotated[str, Field(min_length=32, max_length=32)]  # 应用 Key
    fk_version: Annotated[int, Field(0, ge=0)]  # Flag-Key 版本号
    name: Annotated[str, Field(min_length=2, max_length=64)]  # 应用名称
    notice: Annotated[str, Field('', max_length=512)]  # 应用公告
    force_disabled: Annotated[bool, Field(False)]  # 超管强制禁止使用
    is_disabled: Annotated[bool, Field(False)]  # 禁止使用应用
    allow_recharge: Annotated[bool, Field(False)]  # 是否允许以卡充卡
    max_bind: Annotated[int, Field(1, ge=1, le=255)]  # 卡密可绑定的设备上限
    time_restriction: Annotated[str, Field('00:00:00-23:59:59', min_length=17, max_length=17)]  # 限制使用时段
    unbind_enabled: Annotated[bool, Field(False)]  # 是否允许解绑
    unbind_cost: Annotated[int, Field(0, ge=0, le=65535)]  # 解绑一次扣时多久（分钟）
    max_trial: Annotated[int, Field(0, ge=0, le=255)]  # 可试用次数
    trial_duration: Annotated[int, Field(10, ge=10, le=65535)]  # 试用时长（分钟）
    ping_duration: Annotated[int, Field(10, ge=10, le=65535)]  # 设备心跳失效的时长（分钟）
    card_length: Annotated[int, Field(28, ge=10, le=48)]  # 实际卡密长度
    card_prefix: Annotated[str, Field('', max_length=16)]  # 卡密前缀，默认值为空字符串
    card_rule: Annotated[str, Field(DEFAULT_CARD_RULE, min_length=32, max_length=128)]  # 卡密规则字符
    create_time: Annotated[datetime, Field(default_factory=get_now_date)]  # 创建时间
    update_time: Annotated[datetime, Field(default_factory=get_now_date)]  # 更新时间
    extra_data: Annotated[dict, Field(default_factory=dict, sa_type=JSON)]  # 额外定制数据

    account_id: Annotated[str, Field(default=None, foreign_key='tb_accounts.aid', ondelete='CASCADE')]
    account: 'Account' = Relationship(back_populates='apps')

    card_types: list['CardType'] = Relationship(back_populates='app', cascade_delete=True)

    cards: list['Card'] = Relationship(back_populates='app', cascade_delete=True)

    devices: list['Device'] = Relationship(back_populates='app', cascade_delete=True)

    firewalls: list['FirewallRule'] = Relationship(back_populates='app', cascade_delete=True)

    trail_records: list['TrialRecord'] = Relationship(back_populates='app', cascade_delete=True)

    versions: list['Version'] = Relationship(back_populates='app', cascade_delete=True)

    device_logs: list['DeviceLog'] = Relationship(back_populates='app', cascade_delete=True)

    recharge_logs: list['RechargeLog'] = Relationship(back_populates='app', cascade_delete=True)

    login_times: Optional['AppLoginTimes'] = Relationship(back_populates='app', cascade_delete=True)


@event.listens_for(App, 'before_update', propagate=True)
def update_timestamp(_mapper: Mapper, _connection, target: App):
    """
    在更新记录时，自动将 `update_time` 字段设置为当前时间
    """
    target.update_time = get_now_date()


class CardType(SQLModel, table=True):
    """
    卡种模型
    """
    __tablename__ = "tb_card_types"

    id: Annotated[int, Field(default=None, primary_key=True)]  # 主键
    name: Annotated[str, Field('未知卡种', min_length=2, max_length=64)]  # 卡种名称
    hours: Annotated[int, Field(24, ge=0)]  # 卡种时长
    enabled: Annotated[bool, Field(True)]  # 是否启用
    create_time: Annotated[datetime, Field(default_factory=get_now_date)]  # 创建时间
    update_time: Annotated[datetime, Field(default_factory=get_now_date)]  # 更新时间
    remark: Annotated[str, Field('', max_length=512)]  # 卡种备注

    app_id: Annotated[int, Field(default=None, foreign_key='tb_apps.id', ondelete='CASCADE')]
    app: 'App' = Relationship(back_populates='card_types')

    @staticmethod
    def init_card_types(app_id: int):
        return [
            {
                'name': '半天卡',
                'hours': 12,
                'remark': '应用预设, 仅有半天, 12 小时',
                'app_id': app_id
            },
            {
                'name': '天卡',
                'hours': 24,
                'remark': '应用预设, 仅有 1 天，即 24 小时',
                'app_id': app_id
            },
            {
                'name': '周卡',
                'hours': 168,
                'remark': '应用预设, 仅有 7 天，即 168 小时',
                'app_id': app_id
            },
            {
                'name': '月卡',
                'hours': 672,
                'remark': '应用预设, 仅有 28 天，即 672 小时',
                'app_id': app_id
            },
            {
                'name': '季卡',
                'hours': 2016,
                'remark': '应用预设, 仅有 84 天，即 2016 小时',
                'app_id': app_id
            },
            {
                'name': '年卡',
                'hours': 8064,
                'remark': '应用预设, 仅有 336 天，即 8064 小时',
                'app_id': app_id
            },
            {
                'name': '永久卡',
                'hours': 806400,
                'remark': '应用预设, 理论无限, 仅有 33600 天，即 806400 小时',
                'app_id': app_id
            }
        ]


@event.listens_for(CardType, 'before_update', propagate=True)
def update_timestamp(_mapper: Mapper, _connection, target: CardType):
    """
    在更新记录时，自动将 `update_time` 字段设置为当前时间
    """
    target.update_time = get_now_date()


class Card(SQLModel, table=True):
    """
    卡密模型
    """
    __tablename__ = "tb_cards"

    id: Annotated[int, Field(default=None, primary_key=True)]  # 主键
    card: Annotated[str, Field(min_length=10, max_length=64, index=True)]  # 卡密
    type: Annotated[str, Field(min_length=2, max_length=64)]  # 卡种名称
    hours: Annotated[int, Field(ge=0)]  # 卡种时长
    is_freeze: Annotated[bool, Field(False)]  # 是否冻结
    activate_time: Annotated[datetime, Field(None, nullable=True)]  # 激活时间
    expire_time: Annotated[datetime, Field(None, nullable=True)]  # 激活后的过期时间
    use_app_config: Annotated[bool, Field(True)]  # 是否使用 APP 公共配置
    max_bind: Annotated[int, Field(1, ge=1, le=255)]  # 可绑定的设备上限
    time_restriction: Annotated[str, Field('00:00:00-23:59:59', min_length=17, max_length=17)]  # 限制使用时段
    unbind_enabled: Annotated[bool, Field(False)]  # 是否允许解绑
    unbind_cost: Annotated[int, Field(0, ge=0, le=65535)]  # 解绑一次扣时多久（分钟）
    create_time: Annotated[datetime, Field(default_factory=get_now_date)]  # 创建时间
    update_time: Annotated[datetime, Field(default_factory=get_now_date)]  # 更新时间
    remark: Annotated[str, Field('', max_length=512)]  # 卡密备注
    extra_data: Annotated[dict, Field(default_factory=dict, sa_type=JSON)]  # 额外定制数据

    app_id: Annotated[int, Field(default=None, foreign_key='tb_apps.id', ondelete='CASCADE')]
    app: 'App' = Relationship(back_populates='cards')

    devices: list['Device'] = Relationship(back_populates='card', cascade_delete=True)

    @property
    def status(self):
        """卡密状态（0 未激活, 1 已激活, 2 已冻结, 3 已过期）"""
        if self.is_freeze:
            return CardStatus.FREEZE.value

        if self.activate_time and self.expire_time:
            if get_now_date().timestamp() > self.expire_time.timestamp():
                return CardStatus.EXPIRED.value
            return CardStatus.ACTIVE.value

        return CardStatus.UNKNOWN.value


@event.listens_for(Card, 'before_update', propagate=True)
def update_timestamp(_mapper: Mapper, _connection, target: Card):
    """
    在更新记录时，自动将 `update_time` 字段设置为当前时间
    """
    target.update_time = get_now_date()


class Device(SQLModel, table=True):
    """
    设备模型
    """
    __tablename__ = "tb_devices"

    id: Annotated[int, Field(default=None, primary_key=True)]  # 主键
    code: Annotated[str, Field(min_length=32, max_length=128, index=True)]  # 设备码
    login_time: Annotated[datetime, Field(None, nullable=True)]  # 最后登录时间
    ping_time: Annotated[datetime, Field(None, nullable=True)]  # 最后心跳时间
    ping_ip: Annotated[str, Field('', max_length=64)]  # 最后登录 IP
    token: Annotated[str, Field('', min_length=0, max_length=128)]  # 登录、心跳时记录的 Token
    create_time: Annotated[datetime, Field(default_factory=get_now_date)]  # 创建时间
    update_time: Annotated[datetime, Field(default_factory=get_now_date)]  # 更新时间

    app_id: Annotated[int, Field(default=None, foreign_key='tb_apps.id', ondelete='CASCADE')]
    app: 'App' = Relationship(back_populates='devices')

    card_id: Annotated[int, Field(default=None, foreign_key='tb_cards.id', ondelete='CASCADE')]
    card: 'Card' = Relationship(back_populates='devices')

    @property
    def status(self):
        """设备状态（0 离线, 1 在线）"""
        if self.ping_time is None or not self.token:
            return DeviceStatus.OFFLINE.value

        ping_duration = self.app.ping_duration * 60
        expire_time = self.ping_time.timestamp() + ping_duration
        if get_now_date().timestamp() <= expire_time:
            return DeviceStatus.ONLINE.value

        return DeviceStatus.OFFLINE.value


@event.listens_for(Device, 'before_update', propagate=True)
def update_timestamp(_mapper: Mapper, _connection, target: Device):
    """
    在更新记录时，自动将 `update_time` 字段设置为当前时间
    """
    target.update_time = get_now_date()


class FirewallRule(SQLModel, table=True):
    """
    防火墙模型
    """
    __tablename__ = "tb_firewall_rules"

    id: Annotated[int, Field(default=None, primary_key=True)]  # 主键
    rule_type: Annotated[int, Field(0, ge=0, le=255)]  # 限制类型，0 IP，1 设备码
    rule_match: Annotated[str, Field(min_length=4, max_length=256, index=True)]  # 限制的内容
    is_enabled: Annotated[bool, Field(True)]  # 是否启用
    create_time: Annotated[datetime, Field(default_factory=get_now_date)]  # 创建时间
    update_time: Annotated[datetime, Field(default_factory=get_now_date)]  # 更新时间
    remark: Annotated[str, Field('', max_length=512)]  # 规则备注

    app_id: Annotated[int, Field(default=None, foreign_key='tb_apps.id', ondelete='CASCADE')]
    app: 'App' = Relationship(back_populates='firewalls')


@event.listens_for(FirewallRule, 'before_update', propagate=True)
def update_timestamp(_mapper: Mapper, _connection, target: FirewallRule):
    """
    在更新记录时，自动将 `update_time` 字段设置为当前时间
    """
    target.update_time = get_now_date()


class TrialRecord(SQLModel, table=True):
    """
    试用记录模型
    """
    __tablename__ = "tb_trial_records"

    id: Annotated[int, Field(default=None, primary_key=True)]  # 主键
    device_code: Annotated[str, Field(min_length=32, max_length=128, index=True)]  # 设备码
    trial_ip: Annotated[str, Field('', max_length=64)]  # 设备所在 IP
    trial_time: Annotated[datetime, Field(None, nullable=True)]  # 试用开始时间
    invalid_time: Annotated[datetime, Field(None, nullable=True)]  # 试用结束时间
    request_count: Annotated[int, Field(0, ge=0)]  # 请求次数
    trial_count: Annotated[int, Field(0, ge=0, gt=255)]  # 已试用次数
    create_time: Annotated[datetime, Field(default_factory=get_now_date)]  # 创建时间
    update_time: Annotated[datetime, Field(default_factory=get_now_date)]  # 更新时间

    app_id: Annotated[int, Field(default=None, foreign_key='tb_apps.id', ondelete='CASCADE')]
    app: 'App' = Relationship(back_populates='trail_records')


@event.listens_for(TrialRecord, 'before_update', propagate=True)
def update_timestamp(_mapper: Mapper, _connection, target: TrialRecord):
    """
    在更新记录时，自动将 `update_time` 字段设置为当前时间
    """
    target.update_time = get_now_date()


class Version(SQLModel, table=True):
    """
    应用版本模型
    """
    __tablename__ = "tb_versions"

    id: Annotated[int, Field(default=None, primary_key=True)]  # 主键
    version_code: Annotated[str, Field(max_length=20)]  # 版本号
    download_url: Annotated[str, Field(max_length=512)]  # 下载地址
    description: Annotated[str, Field('', max_length=512)]  # 版本描述
    file_md5: Annotated[str, Field('', min_length=0, max_length=32)]  # 文件 MD5
    is_enforce: Annotated[bool, Field(False)]  # 是否强制更新
    is_publish: Annotated[bool, Field(True)]  # 是否发布
    create_time: Annotated[datetime, Field(default_factory=get_now_date)]  # 创建时间
    update_time: Annotated[datetime, Field(default_factory=get_now_date)]  # 更新时间
    extra_data: Annotated[dict, Field(default_factory=dict, sa_type=JSON)]  # 额外定制数据

    app_id: Annotated[int, Field(default=None, foreign_key='tb_apps.id', ondelete='CASCADE')]
    app: 'App' = Relationship(back_populates='versions')


@event.listens_for(Version, 'before_update', propagate=True)
def update_timestamp(_mapper: Mapper, _connection, target: Version):
    """
    在更新记录时，自动将 `update_time` 字段设置为当前时间
    """
    target.update_time = get_now_date()


class DeviceLog(SQLModel, table=True):
    """
    设备日志模型
    """
    __tablename__ = "tb_device_logs"

    id: Annotated[int, Field(default=None, primary_key=True)]  # 主键
    device_code: Annotated[str, Field(min_length=32, max_length=128, index=True)]  # 设备码
    device_ip: Annotated[str, Field('', max_length=64)]  # 设备 IP
    event_type: Annotated[int, Field(ge=0, le=255)]  # 事件类型
    event_detail: Annotated[str, Field('')]  # 事件详情
    create_time: Annotated[datetime, Field(default_factory=get_now_date)]  # 创建时间

    app_id: Annotated[int, Field(default=None, foreign_key='tb_apps.id', ondelete='CASCADE')]
    app: 'App' = Relationship(back_populates='device_logs')


class RechargeLog(SQLModel, table=True):
    """
    充值日志模型
    """
    __tablename__ = "tb_recharge_logs"

    id: Annotated[int, Field(default=None, primary_key=True)]  # 主键
    origin_card: Annotated[str, Field(min_length=10, max_length=64, index=True)]  # 充值卡密
    used_card: Annotated[str, Field(min_length=10, max_length=64, index=True)]  # 被消耗的卡密
    recharge_ip: Annotated[str, Field('', max_length=64)]  # 充值地 IP
    old_time: Annotated[datetime, Field(None, nullable=True)]  # 原来的时间
    new_time: Annotated[datetime, Field(None, nullable=True)]  # 充值后的时间
    create_time: Annotated[datetime, Field(default_factory=get_now_date)]  # 创建时间

    app_id: Annotated[int, Field(default=None, foreign_key='tb_apps.id', ondelete='CASCADE')]
    app: 'App' = Relationship(back_populates='recharge_logs')


class SystemBoard(SQLModel, table=True):
    """
    系统公告模型
    """
    __tablename__ = "tb_system_boards"

    id: Annotated[int, Field(default=None, primary_key=True)]  # 主键
    content: Annotated[str, Field()]  # 发布的公告内容
    create_time: Annotated[datetime, Field(default_factory=get_now_date)]  # 创建时间

    account_id: Annotated[str, Field(default=None, foreign_key='tb_accounts.aid', ondelete='CASCADE')]
    account: 'Account' = Relationship(back_populates='system_boards')


class AppLoginTimes(SQLModel, table=True):
    """
    App 统计
    """
    __tablename__ = "tb_app_login_times"

    id: Annotated[int, Field(default=None, primary_key=True)]  # 主键
    create_time: Annotated[datetime, Field(default_factory=get_now_date)]  # 登录时间戳

    app_id: Annotated[int, Field(default=None, foreign_key='tb_apps.id', ondelete='CASCADE')]
    app: 'App' = Relationship(back_populates='login_times')
